/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <string.h>
#include <syscalls.h>

#include <ioctl.h>

#include "gfx.h"


static int fb_handle = -1;

/* FIXME: These should be obtained via ioctl */
static int width = 800;
static int height = 600;

int
gfx_open(void)
{
  fb_handle = open("/dev/fb");

  return fb_handle;
}

void
gfx_close(void)
{
  close(fb_handle);
  fb_handle = -1;
}

int
gfx_get_width(void)
{
  return width;
}

int
gfx_get_height(void)
{
  return height;
}

void
gfx_clear(uint16_t color)
{
  ioctl(fb_handle, FB_CLEAR_BUFFER, 0);
}

static void
gfx_seek(int x, int y)
{
  /* By default, the pixel format is RGB16 (5R6G5B).  */
  int rowstride = width * sizeof(uint16_t);

  seek(fb_handle, y * rowstride + x * sizeof(uint16_t), SEEK_SET);
}

void
gfx_set_pixel(int x, int y, uint16_t color)
{
  gfx_seek(x, y);
  write(fb_handle, &color, sizeof(color));
}

void
gfx_fill_rect(int x, int y, int w, int h, uint16_t color)
{
  int i, j;

  if (w < 200)
    {
      uint16_t linebuffer[200];

      for (i = 0; i < w; i++)
        linebuffer[i] = color;

      for (i = 0; i < h; i++)
        {
          gfx_seek(x, y + i);
          write(fb_handle, linebuffer, sizeof(uint16_t) * w);
        }
    }
  else
    {
      for (i = 0; i < h; i++)
        for (j = 0; j < w; j++)
          gfx_set_pixel(x + j, y + i, color);
    }
}

void
gfx_stroke_rect(int x, int y, int w, int h, uint16_t color)
{
  int i;

  for (i = 0; i < w; i++)
    {
      gfx_set_pixel(x + i, y, color);
      gfx_set_pixel(x + i, y + h, color);
    }

  /* FIXME: At least horizontal lines can be drawn with a single
   * write() function call.  Vertical lines as well, but you need
   * a very large buffer for that and you might potentially
   * overwrite other data (unless you account for that being doing
   * a read() first and then putting the line in).
   */
  for (i = 0; i < h; i++)
    {
      gfx_set_pixel(x, y + i, color);
      gfx_set_pixel(x + w, y + i, color);
    }
}

void
gfx_fill_dot(int x, int y, int t, uint16_t color)
{
  int i, j;
  int rt = t / 2;

  for (i = -rt; i < rt; i++)
    for (j = -rt; j < rt; j++)
      gfx_set_pixel(x + i, y + j, color);
}
